package net.chemistry;

import java.io.Serializable;
import java.util.HashSet;

import net.ChemistryException;
import net.util.ArrayList2;
import net.util.CharSequenceUtil;
import net.util.GraphicsUtil;
import net.util.MathUtil;

import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

public class Atom implements Cloneable, Serializable
{
	private static final long serialVersionUID = -889516197662570670L;

	private boolean drawn, mousein, clicked;
	
	private int mx, my;
	
	float showx = 0, showy = 0;
	
	private ElectronCloud einfo;
	
	public final AtomID atomid;
	
	private final float[] color;
	
	public double enegativity;
	
	private ArrayList2<Bond> bonds = new ArrayList2<>(true);
	
	public Atom(AtomID atomid)
	{
		this.atomid = atomid;
		einfo = new ElectronCloud(atomid.number());
		drawn = false;
		
		color = new float[]
		{
				atomid.atomcolor.getRedByte()/100f,atomid.atomcolor.getGreenByte()/100f,atomid.atomcolor.getBlueByte()/100f
		};
		
		//System.out.println(this + " " + einfo);
	}

	public Atom clone()
	{
		Atom a;
		
		try
		{
			a = (Atom) super.clone();
		}
		catch(Exception e)
		{
			return null;
		}
		
		return a;
	}
	
	public void resetDraw()
	{
		drawn = false;
	}
	
	public boolean isBondedTo(Atom otheratom)
	{
		for (Bond b : bonds)
		{
			if(b.hasAtom(otheratom))
			{
				return true;
			}
		}
		return false;
	}
	
	public String toString()
	{
		String s = atomid.toString();
		
		int charge = atomid.number() - einfo.getCharge();
		
		if (charge != 0)
		{
			s += (einfo.getCharge() > 0 ? " +" : " ") + String.valueOf(charge);
		}
		
		s += " @" + Integer.toHexString(hashCode());
		
		return s;
	}
	
	public boolean equals(Object other)
	{
		return super.equals(other);
	}
	
	public boolean equals(Atom other)
	{
		return atomid == other.atomid && einfo.getCharge() == other.einfo.getCharge();
	}
	
	public ElectronCloud getEInfo()
	{
		return einfo;
	}

	public void bond(Bond bond) throws ChemistryException
	{
		if (! bonds.add(bond))
		{
			throw new IllegalArgumentException("Already has bond object");
		}
		
		einfo.share(bond.getBondLevel());
	}
	
	public Atom[] getBondedAtoms()
	{
		HashSet<Atom> result = new HashSet<>();
		
		for (Bond b : bonds)
		{
			result.add(b.getOther(this));
		}	
		
		return result.toArray(new Atom[0]).clone();
	}

	public String info()
	{
		return atomid.toString() + " (" + atomid.abbrev + ")" + 
				"\nNumber: " + atomid.number()  +   
				"\nAtomic mass: " + atomid.AMUweight + " Atomic Mass Units (AMU)" +
				"\nElectronegativity: " + atomid.Xpauling + " Pauling units"/*(" + (char)1000 + ")" */+ 
				"\nElectron configuration: " + einfo.toString();
	}
	
	private void renderBondedAtoms(int x, int y, float angle)
	{
		final float d = (360f / bonds.size());
		double r;
		int x1, x2, y1, y2;
		
		Atom atom;
		
		for(Bond bond : bonds)
		{	
			atom = bond.getOther(this);
			
			r = Math.toRadians(angle);
			
			x1 = (int) Math.round(MathUtil.lx(120, r));
			y1 = (int) Math.round(MathUtil.ly(120, r));
		
			
			if (bond.isIonic())
			{
				System.out.println("Ionic rendering is not currently supported");
			}
			else
			{
				x2 = (int) Math.round(MathUtil.lx(10, r + MathUtil.PI_1_2));
				y2 = (int) Math.round(MathUtil.ly(10, r + MathUtil.PI_1_2));
				
				GL11.glColor4f(color[0],color[1],color[2],0.5f);
				GL11.glBegin(GL11.GL_TRIANGLES);
				GL11.glVertex2d(x,y);
				GL11.glVertex2d(x + x1/2 + x2, y + y1/2 + y2);
				GL11.glVertex2d(x + x1/2 - x2, y + y1/2 - y2);
				GL11.glEnd();
				
				GL11.glColor4f(0, 0, 1, 1);
				GraphicsUtil.renderPolygon(x + x1/2 + x2, y + y1/2 + y2, 4, 5);
			}
			
			if (!atom.drawn)
			{
				atom.render(x + x1, y + y1, (angle + 180) % 360);
			}
			
			angle = (angle + d) % 360;
		}
	}
	
	public boolean renderInfo()
	{
		if ( !mousein)
		{
			showx = showy = 0;
			return false;
		}
		
		mx = Mouse.getX() + 12;
		my = Display.getHeight() - Mouse.getY() + 16;
		
		final String info = info();
		int w = CharSequenceUtil.strWidth(info) * 6;
		int h = CharSequenceUtil.strHeight(info) * 14;
		
		GL11.glColor4f(0.3f, 0, 0.7f, 0.8f);
		
		if (showx < w)
		{
			showx += w/10f;
		}
		if (showy < h)
		{
			showy += h/10f;
		}
		
		GL11.glBegin(GL11.GL_QUADS);
		GL11.glVertex2f(mx, my);
		GL11.glVertex2f(mx + showx, my);
		GL11.glVertex2f(mx + showx, my + showy);
		GL11.glVertex2f(mx, my +showy);
		GL11.glEnd();
		GL11.glColor4f(0, 0, 0,1);
		GL11.glLineWidth(3);
		GL11.glBegin(GL11.GL_LINE_STRIP);
		GL11.glVertex2f(mx, my);
		GL11.glVertex2f(mx + showx, my);
		GL11.glVertex2f(mx + showx, my + showy);
		GL11.glVertex2f(mx, my +showy);
		GL11.glVertex2f(mx, my);
		GL11.glEnd();
		
		GraphicsUtil.DEFAULT_FONT.drawString(mx + 2, my + 2, CharSequenceUtil.strInBox((int)showx/6, (int)showy/14, info));
		GL11.glDisable(GL11.GL_TEXTURE_2D);
		
		return true;
	}
	
	public void render(int x, int y, float angle)
	{	
		drawn = true;
		
		final int l = (einfo.getLevel() * 10) + 20;
		
		mousein = MathUtil.distance(x, y, Mouse.getX(), Display.getHeight() - Mouse.getY()) < l * 0.75;
		clicked = Mouse.isButtonDown(0) && ! Mouse.isGrabbed();
		
		renderBondedAtoms(x, y, angle);
		
		GL11.glColor4f(0,0,0,1f);
		
		GraphicsUtil.renderPolygon(x, y, 24, (clicked && mousein) ? l+5 : l);
		
		einfo.render(x, y, angle);
		
		GL11.glColor4f(color[0],color[1],color[2],0.5f);
		GraphicsUtil.renderPolygon(x, y, 18, (int) Math.round(l * 0.75));
	}
}
